﻿using System;
using System.Collections.Generic;
using System.Linq;
using Algoritmia.Utils.Numerics;

namespace Algoritmia.SemiRings
{
    public class KMinTropicalSemiRing<T> : ISemiRing<IEnumerable<T>,T>
        where T : IComparable<T>
    {
        private readonly IArithmeticsProvider<T> _arithmeticsProvider;
        private readonly int _k;

        public KMinTropicalSemiRing(int k, Func<IArithmeticsProvider<T>> createArithmeticsProvider = null)
        {
            _k = k;
            createArithmeticsProvider = createArithmeticsProvider ?? (() => new BuiltinNumericsArithmeticsProvider<T>());
            _arithmeticsProvider = createArithmeticsProvider();
        }

        #region Implementation of ISemiRing<IEnumerable<T>,in T>

        public IEnumerable<T> Zero
        {
            get { return Enumerable.Repeat(_arithmeticsProvider.MaxValue, _k); }
        }

        public IEnumerable<T> One
        {
            get { return new[] {_arithmeticsProvider.Zero}.Concat(
                                Enumerable.Repeat(_arithmeticsProvider.MaxValue, _k - 1)); }
        }

        public IEnumerable<T> Plus(IEnumerable<T> left, IEnumerable<T> right)
        {
            int i,j,m;
            var array = new T[_k];
            i = j = m = 0;
            var leftList = left.ToList();
            var rightList = right.ToList();
            
            while ( i < leftList.Count && j < rightList.Count && m < _k)
            {
                if ( leftList[i].CompareTo(rightList[j]) < 0)
                {
                    array[m++] = leftList[i++];
                }
                else
                {
                    array[m++] = rightList[j++];
                }
            }

            while ( i < leftList.Count && m < _k)
            {
                array[m++] = leftList[i++];
                m++;
            }

            while (j < rightList.Count && m < _k)
            {
                array[m++] = rightList[j++];
                m++;
            }
            return array;
        }

        public IEnumerable<T> Times(IEnumerable<T> left, T right)
        {
            return left.Select(e => (T) _arithmeticsProvider.Add(e, right));
        }

        #endregion
    }
}
